package org.ghost.springboot.demo.config;

import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.plugins.SqlExplainInterceptor;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.yaml.sharding.YamlShardingConfiguration;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

@Configuration
public class CommonConfig {
    private static final String SHARDING_DATA_SOURCE_YML_PATH = "sharding/dataSource.yml";

    @Autowired
    private MybatisProperties properties;

    @Primary
    @Bean
    public DataSource createDataSource() throws SQLException, IOException {
        Resource resource = new ClassPathResource(SHARDING_DATA_SOURCE_YML_PATH);
        InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream(), "UTF-8");

        YamlShardingConfiguration yamlShardingConfiguration = new Yaml(new Constructor(YamlShardingConfiguration.class)).loadAs(inputStreamReader, YamlShardingConfiguration.class);

        return ShardingDataSourceFactory.createDataSource(yamlShardingConfiguration.getDataSources(),
                yamlShardingConfiguration.getShardingRule().getShardingRuleConfiguration(),
                yamlShardingConfiguration.getShardingRule().getConfigMap(),
                yamlShardingConfiguration.getShardingRule().getProps());
    }

//    @ConditionalOnMissingBean
//    public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
//        JdbcTemplate jdbcTemplate = new JdbcTemplate();
//        jdbcTemplate.setDataSource(dataSource);
//        return jdbcTemplate;
//    }

    @ConditionalOnMissingBean
    public NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }

    @Bean
    public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean(DataSource dataSource) {
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
        mybatisPlus.setDataSource(dataSource);
        mybatisPlus.setVfs(SpringBootVFS.class);

        //MyBatisPlus全局配置
        GlobalConfiguration globalConfig = new GlobalConfiguration();
        mybatisPlus.setGlobalConfig(globalConfig);

        // 使用MybatisPlus设置, 忽略配置文件configuration中除了variables以外的其他属性
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        mybatisConfiguration.setMapUnderscoreToCamelCase(true);
        mybatisPlus.setConfiguration(mybatisConfiguration);
        // 插件设置
        setPlugins(mybatisPlus);
        // xmlMapper文件读入
        mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations());
        // 其他属性设置
        mybatisPlus.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());

        return mybatisPlus;

    }

    private void setPlugins(MybatisSqlSessionFactoryBean mybatisPlus) {
        //自定义拦截器
        org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration();
        Properties variables = configuration != null ? configuration.getVariables() : null;

        boolean hasPaginationInterceptor = variables == null || Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("paginationInterceptor", Boolean.TRUE).toString());
        boolean hasPerformanceInterceptor = variables != null && Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("performanceInterceptor", Boolean.FALSE).toString());
        boolean hasOptimisticLockerInterceptor = variables != null && Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("optimisticLockerInterceptor", Boolean.FALSE).toString());
        boolean hasSqlExplainInterceptor = variables != null && Boolean.parseBoolean(this.properties.getConfiguration().getVariables().getOrDefault("sqlExplainInterceptor", Boolean.FALSE).toString());

        List<Interceptor> interceptorList = new ArrayList<Interceptor>();
        if (hasPaginationInterceptor) {
            PaginationInterceptor paginationInterceptor = this.getPaginationInterceptor();
            if (variables != null) {
                paginationInterceptor.setProperties(variables);
            }
            interceptorList.add(paginationInterceptor);
        }
        if (hasPerformanceInterceptor) {
            PerformanceInterceptor performanceInterceptor = this.getPerformanceInterceptor();
            performanceInterceptor.setProperties(variables);
            interceptorList.add(performanceInterceptor);
        }
        if (hasOptimisticLockerInterceptor) {
            OptimisticLockerInterceptor optimisticLockerInterceptor = this.getOptimisticLockerInterceptor();
            optimisticLockerInterceptor.setProperties(variables);
            interceptorList.add(optimisticLockerInterceptor);
        }
        if (hasSqlExplainInterceptor) {
            SqlExplainInterceptor sqlExplainInterceptor = this.getSqlExplainInterceptor();
            sqlExplainInterceptor.setProperties(variables);
            interceptorList.add(sqlExplainInterceptor);
        }
        mybatisPlus.setPlugins(interceptorList.stream().filter(Objects::nonNull).toArray(Interceptor[]::new));
    }

    /**
     * 分页插件
     *
     * @return
     */
    private PaginationInterceptor getPaginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType("mysql");
        return paginationInterceptor;
    }

    /**
     * 性能分析插件(正式机不要用)
     *
     * @return
     */
    private PerformanceInterceptor getPerformanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setFormat(true);
        performanceInterceptor.setMaxTime(3000L);
        return performanceInterceptor;
    }

    /**
     * 乐观锁插件com.baomidou.mybatisplus.annotations.Version注解
     *
     * @return
     */
    private OptimisticLockerInterceptor getOptimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }

    /**
     * 执行计划
     *
     * @return
     */
    private SqlExplainInterceptor getSqlExplainInterceptor() {
        SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
        sqlExplainInterceptor.setStopProceed(true);
        return sqlExplainInterceptor;
    }

}
